from __future__ import print_function
import ctypes
from _ctypes import _SimpleCData, Array

liblpa = ctypes.cdll.LoadLibrary("liblpa")

def lpaAdd(left, right):
    length = min(len(left), len(right))
    if not isinstance(left, (_SimpleCData, Array)) or \
        not isinstance(right, (_SimpleCData, Array)):
        if isinstance(left[0], (int, long)) and \
            isinstance(right[0], (int, long)):
            import math
            expon = math.log(max(left + right), 2)
            # One less to support the operation
            if expon <= 7:
                vartype = ctypes.c_ubyte
            elif expon <= 15:
                vartype = ctypes.c_ushort
            elif expon <= 31:
                vartype = ctypes.c_ulong
            elif expon <= 63:
                vartype = ctypes.c_ulonglong
            else:
                raise Exception("Too large value! (Will convert appropriately "
                                "in a future release)")
            cleft = (vartype * length)(*left)
            cright = (vartype * length)(*right)
        liblpa.lpaAdd(cleft,
                      cright,
                      length,
                      ctypes.sizeof(vartype))
        return cleft
    liblpa.lpaAdd(left,
                  right,
                  length,
                  ctypes.sizeof(left) // length)
    return left

if __name__ == "__main__":
    # Launch benchmarks
    import time
    import sys
    import random

    SIZE = int(sys.argv[1]) if len(sys.argv) > 1 else 1000000
    
    print("Starting liblpa benchmark with random arrays of length: %i" % SIZE)

    # Initialize arrays
    print("Array initialisation...")
    MAX_NUM = 65535
    MAX_NUM = 16000
    arr1 = [random.randint(0, MAX_NUM) for i in range(SIZE)]
    arr2 = [random.randint(0, MAX_NUM) for i in range(SIZE)]
    arr3 = [0 for i in range(SIZE)]
    print("Arrays initialized. Beginning benchmark...")

    # Benchmark Python
    a = time.time()
    ans = [arr1[i] + arr2[i] for i in range(SIZE)]
    print("Python variables: \t\t%.0fms" % (float(time.time()-a) * 1000))
    a = time.time()
    cArr1 = (ctypes.c_ulong * SIZE)(*arr1)
    cArr2 = (ctypes.c_ulong * SIZE)(*arr2)
    ansc = [cArr1[i] + cArr2[i] for i in range(SIZE)]
    print("C variables (conv): \t\t%.0fms" % (float(time.time()-a) * 1000))

    # Benchmark liblpa
    a = time.time()
    cArr1 = lpaAdd(cArr1, cArr2)
    print("liblpa C vars: \t\t\t%.0fms" % (float(time.time()-a) * 1000))
    a = time.time()
    lpaPythonAns = lpaAdd(arr1, arr2)
    print("liblpa Python vars (conv): \t%.0fms" % (float(time.time()-a) * 1000))

    print("Validating results...")
    # Verify data
    if any([ans[i] != cArr1[i] for i in range(SIZE)]):
        print("C Array was not copied correctly, liblpa error occured.")
    if any([ans[i] != lpaPythonAns[i] for i in range(SIZE)]):
        print("Python Array was not copied correctly, liblpa error occured.")
    print("Results validation done!")